﻿using System;
using System.Collections.Generic;
using System.Linq;
using LuxuryResort.BIZ;
using LuxuryResort.Entity;
using LuxuryResort.Service.Mapping;
using Reservation = LuxuryResort.Service.Mapping.Reservation;
using Room = LuxuryResort.Service.Mapping.Room;

namespace LuxuryResort.Service
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
    public class RoomService : IRoomService
    {
        private readonly IFoodBeverageBiz _fbBiz = new FoodBeverageBiz();
        private readonly IRoomBiz _iRoomBiz = new RoomBiz();
        private readonly IFoodOrderBiz _orderBiz = new FoodOrderBiz();
        private readonly IReservationBiz _reservationBiz = new ReservationBiz();

        public List<Room> GetAvailableRooms(Guid userId, Guid resortId, DateTime checkin, DateTime checkout,
            string roomType)
        {
            IOrderedEnumerable<Entity.Room> rooms =
                _iRoomBiz.GetAvailableRooms(userId, resortId, checkin, checkout, roomType).OrderBy(x => x.RoomNo);

            return rooms.Select(Room.MapToRoom).ToList();
        }

        public List<Room> GetRooms(Guid userId, Guid resortId)
        {
            IOrderedEnumerable<Entity.Room> rooms = _iRoomBiz.GetRooms(userId, resortId).OrderBy(x => x.RoomNo);

            return rooms.Select(Room.MapToRoom).ToList();
        }

        public Room GetRoom(Guid roomId, Guid userId, Guid resortId)
        {
            Entity.Room efRoom = _iRoomBiz.GetRoomById(roomId, userId, resortId);
            return Room.MapToRoom(efRoom);
        }

        public void BookRoom(Reservation r, Guid userId, Guid resortId)
        {
            Entity.Reservation reservation = Reservation.MapToReservation(r);
            _reservationBiz.BookRoom(reservation, userId, resortId);
        }

        public void OrderFood(List<FoodOrder> fos, Guid userId, Guid resortId)
        {
            List<Food_Beverage_Order> lst = fos.Select(FoodOrder.MapToFoodOrder).ToList();

            _orderBiz.OrderFood(lst, userId, resortId);
        }

        public List<FoodBeverage> GetAllFood(Guid userId, Guid resortId)
        {
            IOrderedEnumerable<Food_Beverage> fbs =
                _fbBiz.GetAllFood(userId, resortId).OrderBy(x => x.Type).ThenBy(x => x.Name);

            return fbs.Select(FoodBeverage.MapToServiceFoodBeverage).ToList();
        }

        public List<LRMPReport> GetReport(Guid userId, Guid resortId)
        {
            var result = new List<LRMPReport>();

            result.AddRange(GetFromRooms(userId, resortId));
            result.AddRange(GetFromFoodOrder(userId, resortId));

            return result;
        }

        public List<LRMPNationalReport> GetNationalReport(Guid userId)
        {
            List<KeyValuePair<string, Entity.Reservation>> reservations = _reservationBiz.GetAll(userId, new Guid());
            List<KeyValuePair<string, Food_Beverage_Order>> orders = _orderBiz.GetAll(userId, new Guid());
            var lst = new List<LRMPNationalReport>();

            BuildFoodOrderReport(orders, lst);
            BuildReservationReport(reservations, lst);

            return lst;
        }

        public int InsertFood(FoodBeverage food, Guid userId, Guid resortId)
        {
            Food_Beverage foodBeverage = FoodBeverage.MapToServiceFoodBeverage(food);
            return _fbBiz.InsertFood(foodBeverage, userId, resortId);
        }

        public int UpdateFood(FoodBeverage food, Guid userId, Guid resortId)
        {
            Food_Beverage foodBeverage = FoodBeverage.MapToServiceFoodBeverage(food);
            return _fbBiz.UpdateFood(foodBeverage, userId, resortId);
        }


        public int DeleteFood(Guid foodId, Guid userId, Guid resortId)
        {
            return _fbBiz.DeleteFood(foodId, userId, resortId);
        }


        public List<KeyValuePair<string, Entity.Reservation>> GetAllReservations(Guid userId, Guid resortId)
        {
            return _reservationBiz.GetAll(userId, resortId);
        }

        public List<Reservation_Room> GetAllReservationRooms(Guid userId, Guid resortId)
        {
            throw new NotImplementedException();
        }


        public List<Reservation> GetReservations(Guid userId, Guid resortId)
        {
            //var lst = _reservationBiz.GetReservations(userId, resortId);
            List<KeyValuePair<string, Entity.Reservation>> lst = _reservationBiz.GetAll(userId, resortId);

            return lst.Select(Reservation.MapToWcfReservation).ToList();
        }


        public void DeleteReservation(Guid reservationId, Guid roomId, Guid userId, Guid resortId)
        {
            _reservationBiz.DeleteReservation(reservationId, roomId, userId, resortId);
        }


        public void UpdateReservation(Reservation reservation, Guid userId, Guid resortId)
        {
            Entity.Reservation entity = Reservation.MapToReservation(reservation);
            _reservationBiz.UpdateReservation(entity, userId, resortId);
        }

        public List<LRMPOccupancyReport> GetOccupancyReport(Guid userId, Guid resortId, DateTime fromDate,
            DateTime toDate)
        {
            List<Entity.Reservation> reservations = _reservationBiz.GetReservations(userId, resortId);
            var results = new List<LRMPOccupancyReport>();

            if (reservations.Count > 0)
            {
                IEnumerable<Entity.Reservation> validReservations =
                    reservations.Where(x => !x.Status.Equals("Cancelled")
                                            && ((x.CheckIn >= fromDate && x.CheckOut <= toDate)
                                                || (x.CheckIn <= fromDate && x.CheckOut >= fromDate)
                                                || (x.CheckIn <= toDate && x.CheckOut >= toDate))
                        );

                List<LRMPOccupancyReport> occupancyReports = (from validReservation in validReservations
                    let inputFromDate = fromDate >= validReservation.CheckIn ? fromDate : validReservation.CheckIn
                    let inputToDate = toDate <= validReservation.CheckOut ? toDate : validReservation.CheckOut
                    select new LRMPOccupancyReport
                    {
                        RoomNo = validReservation.Room.RoomNo,
                        RoomPrice = validReservation.Room.PriceUnit,
                        TotalDays = inputToDate.Value.Subtract(inputFromDate.Value).Days + 1
                    }).ToList();

                var calculatedOccupancy = (from o in occupancyReports
                    group o by o.RoomNo
                    into g
                    select new {RoomNo = g.Key, Total = g.Sum(o => o.TotalDays)});

                results.AddRange(calculatedOccupancy.Select(occupancy => new LRMPOccupancyReport
                {
                    RoomNo = occupancy.RoomNo,
                    TotalDays = occupancy.Total
                }));
            }

            return results;
        }

        private void BuildFoodOrderReport(IEnumerable<KeyValuePair<string, Food_Beverage_Order>> orders,
            List<LRMPNationalReport> lst)
        {
            lst.AddRange(orders.Select(o => new LRMPNationalReport
            {
                ResortName = o.Key,
                LocalReport = new LRMPReport
                {
                    Category = o.Value.Food_Beverage.Type,
                    CustomerName = o.Value.CustomerName,
                    FBName = o.Value.Food_Beverage.Name,
                    FBType = o.Value.Food_Beverage.Type,
                    PaymentAmount = o.Value.Food_Beverage.PriceUnit.Value*o.Value.Quantity.Value,
                    Quantity = o.Value.Quantity.Value
                }
            }));
        }

        private void BuildReservationReport(IEnumerable<KeyValuePair<string, Entity.Reservation>> reservations,
            List<LRMPNationalReport> lst)
        {
            lst.AddRange(reservations.Select(r => new LRMPNationalReport
            {
                ResortName = r.Key,
                LocalReport = new LRMPReport
                {
                    Category = "Room",
                    CheckIn = r.Value.CheckIn.Value,
                    CheckOut = r.Value.CheckOut.Value,
                    CustomerName = r.Value.CustomerName,
                    PaymentAmount = r.Value.Room.PriceUnit.Value*r.Value.Quantity,
                    Quantity = r.Value.Quantity,
                    RoomNo = r.Value.Room.RoomNo.Value
                }
            }));
        }

        private IEnumerable<LRMPReport> GetFromRooms(Guid userId, Guid resortId)
        {
            List<KeyValuePair<string, Entity.Reservation>> reservertions = _reservationBiz.GetAll(userId, resortId);
            return reservertions.Select(r => new LRMPReport
            {
                Category = "Room",
                CheckIn = r.Value.CheckIn.Value,
                CheckOut = r.Value.CheckOut.Value,
                CustomerName = r.Value.CustomerName,
                PaymentAmount = r.Value.Room.PriceUnit.Value*r.Value.Quantity,
                Quantity = r.Value.Quantity,
                RoomNo = r.Value.Room.RoomNo.Value
            }).ToList();
        }

        private IEnumerable<LRMPReport> GetFromFoodOrder(Guid userId, Guid resortId)
        {
            List<KeyValuePair<string, Food_Beverage_Order>> foodOrders = _orderBiz.GetAll(userId, resortId);
            return foodOrders.Select(o => new LRMPReport
            {
                Category = o.Value.Food_Beverage.Type,
                CustomerName = o.Value.CustomerName,
                FBName = o.Value.Food_Beverage.Name,
                FBType = o.Value.Food_Beverage.Type,
                PaymentAmount = o.Value.Food_Beverage.PriceUnit.Value*o.Value.Quantity.Value,
                Quantity = o.Value.Quantity.Value
            }).ToList();
        }
    }
}